/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package server.votesending;

import data.Tool;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.DataInputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.xml.bind.DatatypeConverter;
import server.votesending.shamirsecret.GenerateKeysAndSecret;

/**
 *
 * @author Maciek
 */
public class Counter {

    GenerateKeysAndSecret gask;

    public Counter(GenerateKeysAndSecret gask) {
        this.gask = gask;
    }

    private String bytesToString(byte[] bytes) {
        return DatatypeConverter.printBase64Binary(bytes);
    }

    private byte[] stringToBytes(String string) {
        return DatatypeConverter.parseBase64Binary(string);
    }

    public void parse(String line) {
        try {
            String[] g = line.split(" ");
            byte[] encrypted = stringToBytes(g[1]);
            byte[] decrypted = gask.rsaDecrypt(encrypted);
            System.out.println(g[0] + ": said " + bytesToString(decrypted));
        } catch (NoSuchAlgorithmException ex) {
            Logger.getLogger(Counter.class.getName()).log(Level.SEVERE, null, ex);
        } catch (NoSuchPaddingException ex) {
            Logger.getLogger(Counter.class.getName()).log(Level.SEVERE, null, ex);
        } catch (InvalidKeyException ex) {
            Logger.getLogger(Counter.class.getName()).log(Level.SEVERE, null, ex);
        } catch (IllegalBlockSizeException ex) {
            Logger.getLogger(Counter.class.getName()).log(Level.SEVERE, null, ex);
        } catch (BadPaddingException ex) {
            Logger.getLogger(Counter.class.getName()).log(Level.SEVERE, null, ex);
        }


    }
    // 

    private ReceivedVote getReceivedVote(String line) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException, UnsupportedEncodingException {
        String[] g = line.split(" ");
        byte[] encrypted = stringToBytes(g[1]);
        byte[] decrypted = gask.rsaDecrypt(encrypted);
        // tutja trzeba prawdziwie kod odczytywać z decrypted, zakladajac ze zostalo wyslane: signUnblindVote
        String s = bytesToString(decrypted);
        GenerateKeysAndSecret gen = new GenerateKeysAndSecret();
        gen.restorePubKeyX509("key");
        BigInteger n = gen.getPublicKey().getModulus();
        BigInteger e = gen.getPublicKey().getPublicExponent();
        String[] v = Tool.signToClean(s, e, n).split(";");
        return new ReceivedVote(Long.parseLong(g[0]), Integer.valueOf(v[0]), new BigInteger(v[2].getBytes()));
    }

    public void countVotes(String voteList, String result, String allvotes) throws FileNotFoundException, IOException {
        decypher();

        countVotes();
        writeCountedVotes(voteList);
        writeResults(result);
        writeDecyphrableVotes(allvotes);
    }
    ArrayList<ReceivedVote> receivedVotes;

    public void decypher() {
        FileInputStream fstream = null;
        try {
            receivedVotes = new ArrayList<ReceivedVote>();
            fstream = new FileInputStream("mydatabase.txt");
            // Get the object of DataInputStream
            DataInputStream in = new DataInputStream(fstream);
            BufferedReader br = new BufferedReader(new InputStreamReader(in));
            String strLine;
            //Read File Line By Line
            while ((strLine = br.readLine()) != null) {
                // Print the content on the console
                try {
                    receivedVotes.add(getReceivedVote(strLine));
                    parse(strLine);

                } catch (Exception e) {
                    System.err.println("Failed to decypher vote : " + strLine);
                    System.err.println(e.toString());
                    parse(strLine);
                }
            }
            //Close the input stream
            in.close();
        } catch (IOException ex) {
            Logger.getLogger(Counter.class.getName()).log(Level.SEVERE, null, ex);
        } finally {
            try {
                fstream.close();
            } catch (IOException ex) {
                Logger.getLogger(Counter.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }

    class ReceivedVoteComparator implements Comparator {

        @Override
        public int compare(Object o1, Object o2) {
            ReceivedVote r1 = (ReceivedVote) o1;
            ReceivedVote r2 = (ReceivedVote) o2;
            if (r1.timestamp < r2.timestamp) {
                return -1;
            }
            if (r1.timestamp > r2.timestamp) {
                return 1;
            } else {
                return 0;
            }
        }
    }
    Map<BigInteger, Integer> map;
    Map<Integer, Integer> results;

    void countVotes() {
        System.out.println("Size of receivec votes : " + receivedVotes.size());
        Collections.sort(receivedVotes, new ReceivedVoteComparator());
        map = new HashMap<BigInteger, Integer>();

        for (ReceivedVote rv : receivedVotes) {
            if (!map.containsKey(rv.getVoteCard())) {
                map.put(rv.getVoteCard(), new Integer(rv.getCandidateNumber()));
            }
        }
        System.out.println("Oszukuje wrzucam głos : 10 na 1 ");
        map.put(BigInteger.TEN, new Integer(1));
        System.out.println("Szie of good votes : " + map.size());
        results = new HashMap<Integer, Integer>();
        for (Map.Entry<BigInteger, Integer> entry : map.entrySet()) {
            if (results.containsKey(entry.getValue())) {
                entry.setValue(entry.getValue() + 1);
            } else {
                results.put(entry.getValue(), new Integer(1));
            }
        }
        System.out.println("Szie of different candidates voted on : " + results.size());
    }

    void writeCountedVotes(String countedVotesFile) {


        try {
            BufferedWriter out = new BufferedWriter(new FileWriter(countedVotesFile));
            for (Map.Entry<BigInteger, Integer> entry : map.entrySet()) {
                out.write(entry.getValue() + " " + entry.getKey());
                out.newLine();
            }
            out.close();

        } catch (IOException ex) {
            Logger.getLogger(Counter.class.getName()).log(Level.SEVERE, null, ex);
        }

    }

    void writeResults(String resultFile) {
        try {
            BufferedWriter out = new BufferedWriter(new FileWriter(resultFile));
            for (Map.Entry<Integer, Integer> entry : results.entrySet()) {
                out.write(+entry.getKey() + " : " + entry.getValue());
                out.newLine();
            }
            out.close();
        } catch (IOException ex) {
            Logger.getLogger(Counter.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    void writeDecyphrableVotes(String allvotes) {
        try {
            BufferedWriter out = new BufferedWriter(new FileWriter(allvotes));
            for (ReceivedVote rv : receivedVotes) {
                out.write("timestamp : " + rv.getTimestamp() + " identifier " + rv.getVoteCard() + " candidate : " + rv.candidateNumber);
                out.newLine();
            }
            out.close();

        } catch (IOException ex) {
            Logger.getLogger(Counter.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
}
